package setStructDefault

import (
	"gitee.com/zibianqu/fyzdb/lib"
	"reflect"
	"strconv"
)

type ISet interface {
	// 设置结构体默认值
	ReflectSetValue() interface{}
	HandleReflectSetValue(pv *reflect.Value, pt reflect.Type)
}

type setImpl struct {
	p interface{}
}

// 通过标签给结构体属性设置默认值
func Handle(p interface{}) ISet {
	set := setImpl{p: p}
	return &set
}

/**
|---------------------------------
| 根据tags给结构设置默认值
| user: fcl date: 2022/6/30
|---------------------------------
| 参考：https://www.coder.work/article/7190356
| 第一次通话Elem()取消引用指向 interface{} 的指针.第二次调用Elem()获取接口(interface)中包含的值。
| 有了这个变化， panic 是 reflect.Value.SetString using unaddressable value .
| 应用程序不能直接在接口(interface)中包含的结构值上设置字段，因为接口(interface)中包含的值是不可寻址的。
| 将struct值复制到临时变量中，设置临时变量中的字段，将临时变量复制回接口(interface)。
*/
func (s *setImpl) ReflectSetValue() interface{} {
	p := s.p
	piv := reflect.ValueOf(&p).Elem()
	// 重新根据原结构生成一个新的反射
	pv := reflect.New(piv.Elem().Type()).Elem()
	// 把原反射的值赋值新的反射值
	pv.Set(piv.Elem())
	pt := reflect.TypeOf(p)
	s.HandleReflectSetValue(&pv, pt)
	// 在赋值回去
	piv.Set(pv)
	return p
}

// 解决嵌套结构体
func (s *setImpl) HandleReflectSetValue(pv *reflect.Value, pt reflect.Type) {
	for i := 0; i < pt.NumField(); i++ {
		tf := pt.Field(i)
		tks := tf.Type.Kind().String()
		vf := pv.FieldByName(tf.Name)
		// 解决嵌套结构体
		if tks == "struct" {
			pvI := reflect.New(tf.Type).Elem()
			ptI := reflect.TypeOf(vf.Interface())
			s.HandleReflectSetValue(&pvI, ptI)
			vf.Set(pvI)
			continue
		}
		// 判断获取默认值， 如果有需要可设置跳过赋值属性
		dv, df := tf.Tag.Lookup("default")
		if !df {
			continue
		}
		switch tks {
		case "int":
			div, err := strconv.ParseInt(dv, 10, 64)
			lib.ThrowError(err)
			vf.SetInt(div)
		case "uint":
			div, err := strconv.ParseUint(dv, 10, 64)
			lib.ThrowError(err)
			vf.SetUint(div)
		case "string":
			vf.SetString(dv)
		case "boolean":
			bv, err := strconv.ParseBool(dv)
			lib.ThrowError(err)
			vf.SetBool(bv)
			// todo 暂时只处理这些
		}
	}
}
